﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Linq;
using Grundy.CryptographicServices;
using Grundy.Entity;
using Grundy.Common;
using System.Linq;
using Grundy.Message;

namespace Grundy.Licensing.Server
{
    public class LicenseStore : ICustomerKeyStore
    {
        private readonly LicenseManager _licenseManager;
        private readonly NodeIdentityComparer _nodeIdentityComparer;
        private readonly CryptographicServicesProvider _serverCryptographicServicesProvider;
        List<CustomerLicenseEntitlement> _selfCple = new List<CustomerLicenseEntitlement>();
        List<CustomerLicenseEntitlement> _otherCple = new List<CustomerLicenseEntitlement>();

        protected LicenseStore(){}

        public LicenseStore(LicenseManager licenseManager, NodeIdentityComparer nodeIdentityComparer, CryptographicServicesProvider serverCryptographicServicesProvider)
        {
            _licenseManager = licenseManager;
            _nodeIdentityComparer = nodeIdentityComparer;
            _serverCryptographicServicesProvider = serverCryptographicServicesProvider;
        }

        public List<CustomerLicenseEntitlement> Add(string license)
        {
            CustomerEntitlement c;
            var loadedCple = new List<CustomerLicenseEntitlement>();
            try
            {
                var container = license.Deserialize<Container>();
                c = _licenseManager.CreateCustomerEntitlementFromLicense(container);
                var encryptedCustomerKey =  c.Customer.Key;
                var decryptedCustomerKey =  _serverCryptographicServicesProvider.DecryptData(encryptedCustomerKey.ToByteArray());
                _customerKeys.Add(Tuple.Create(c.Customer.Id, c.Customer.Name, decryptedCustomerKey));
                
                // Only load floating licenses
                var cple = c.LicenseEntitlements
                           .Where(i => i.License is FloatingLicense)
                           .Select(i => GetCustomerProductLicenseEntitlement(c, i, decryptedCustomerKey))
                           .ToList();
                loadedCple = cple;
            }
            catch (Exception ex)
            {
                throw new InvalidLicenseFormatException(license, ex);
            }
            if (c == null)
            {
                throw new InvalidLicenseFormatException(license, null);
            }

            foreach (var cple in loadedCple)
            {
                if(_nodeIdentityComparer.Matches(cple.NodeIdentity))
                {
                    if (!_selfCple.Contains(cple))
                    {
                        _selfCple.Add(cple);
                    }
                }
                else if (!_otherCple.Contains(cple))
                {
                    _otherCple.Add(cple);
                }
            }
            
            return loadedCple;
        }

        private CustomerLicenseEntitlement GetCustomerProductLicenseEntitlement(CustomerEntitlement c, LicenseEntitlement i, byte[] decryptedCustomerKey)
        {
            return new CustomerLicenseEntitlement(c.Customer.Id, c.Customer.Name, i, decryptedCustomerKey);
        }

        public virtual IEnumerable<CustomerLicenseEntitlement> OtherCustomerProductLicenseEntitlements { get { return _otherCple; } }
        public virtual IEnumerable<CustomerLicenseEntitlement> SelfCustomerProductLicenseEntitlements { get { return _selfCple; } }
        private List<Tuple<Guid,string, byte[]>> _customerKeys = new List<Tuple<Guid, string, byte[]>>();

        public virtual Tuple<Guid,string, byte[]> GetCustomerKey(Guid customerId = default(Guid), string customerName = null)
        {
            if(customerId == Guid.Empty && customerName.IsNullOrEmpty())
                throw new CustomerNotFoundException(customerId, customerName);
            var tuple = _customerKeys.FirstOrDefault(i => (customerId == Guid.Empty ? true : (i.Item1 == customerId)) && (customerName.IsNullOrEmpty() ? true : (i.Item2.EqualsIgnoreCase(customerName))));
            if (tuple == null)
                throw new CustomerNotFoundException(customerId, customerName);
            return Tuple.Create(tuple.Item1, tuple.Item2, tuple.Item3);
        }
    }
}